﻿using Levfier.Proccessors.Parameters;
using System;

namespace Levfier.Proccessors.Filters
{
    /// <summary>
    /// FIRの基本クラス
    /// </summary>
    [Serializable]
    abstract public class FIR : Proccessor
    {
        /// <summary>
        /// フィルタ長さ
        /// </summary>
        protected ProccessParameter filterLengthParam;

        /// <summary>
        /// カットオフ周波数
        /// </summary>
        protected ProccessParameter cutOffFreqParam;

        /// <summary>
        /// 入力リングバッファ
        /// </summary>
        protected double[] inputRingBuffer;

        /// <summary>
        /// 入力リングバッファのインデックス
        /// </summary>
        protected int inputRingBufferIndex;

        /// <summary>
        /// 係数
        /// </summary>
        protected double[] coefficientValues;

        /// <summary>
        /// ロック用のオブジェクト
        /// </summary>
        private object lockObj = new object();

        /// <summary>
        /// 入力を線形増幅する
        /// </summary>
        /// <param name="input">入力</param>
        /// <param name="output">出力</param>
        protected override void InnerProccess(double[] inputBuffer, double[] outputBuffer)
        {
            double temp;

            lock (lockObj)
            {
                for (int i = 0; i < inputBuffer.Length; i++)
                {
                    temp = 0.0;

                    this.AddInputValueToBuffer(inputBuffer[i]);

                    for (int j = 0; j < this.inputRingBuffer.Length; j++)
                    {
                        temp += this.coefficientValues[j] * this.GetValueFromBuffer(j);
                    }

                    outputBuffer[i] = temp;
                }
            }
        }

        /// <summary>
        /// パラメータを初期化する
        /// </summary>
        protected override void InitParameters()
        {
            this.filterLengthParam = new ProccessParameter();
            this.filterLengthParam.Name = "Tap Num";
            this.filterLengthParam.Value = 31.0d;
            this.filterLengthParam.MaxValue = 199.0d;
            this.filterLengthParam.MinValue = 3.0d;
            this.filterLengthParam.Step = 2.0d;
            this.filterLengthParam.ValueChanged += InitFilter;
            this.parameters.Add(this.filterLengthParam.Name, this.filterLengthParam);

            this.cutOffFreqParam = new ProccessParameter();
            this.cutOffFreqParam.Name = "Freq[Hz]";
            this.cutOffFreqParam.Value = 1000.0d;
            this.cutOffFreqParam.MaxValue = 16000.0d;
            this.cutOffFreqParam.MinValue = 10.0d;
            this.cutOffFreqParam.Step = 10.0d;
            this.cutOffFreqParam.ValueChanged += InitFilter;
            this.parameters.Add(this.cutOffFreqParam.Name, this.cutOffFreqParam);

            this.InitFilter(null);
        }

        /// <summary>
        /// パラメータが変更された際の動作
        /// フィルタを作り直す。
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        abstract protected void InitFilter(object sender);

        /// <summary>
        /// リングバッファに値を格納する
        /// </summary>
        /// <param name="value">格納する値</param>
        private void AddInputValueToBuffer(double value)
        {
            this.inputRingBuffer[this.inputRingBufferIndex % this.inputRingBuffer.Length] = value;
            inputRingBufferIndex = (inputRingBufferIndex + 1) % this.inputRingBuffer.Length;
        }

        /// <summary>
        /// リングバッファから値を取り出す
        /// 最新の値から順に格納
        /// </summary>
        /// <param name="index">取り出す値のインデックス</param>
        /// <returns>値</returns>
        private double GetValueFromBuffer(int index)
        {
            return this.inputRingBuffer[(this.inputRingBufferIndex - index + this.inputRingBuffer.Length) % this.inputRingBuffer.Length];
        }
    }
}
